﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;


namespace DiscoveryLogic.UI.Layouts.Controls
{

    public class GraphSlider : Control
    {
        public GraphSlider()
        {
            DefaultStyleKey = typeof(GraphSlider);
        }

        #region Dependency Properties

        public string CompleteDescriptionFormat
        {
            get { return (string)GetValue(CompleteDescriptionFormatProperty); }
            set { SetValue(CompleteDescriptionFormatProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CompleteDescriptionFormat.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CompleteDescriptionFormatProperty =
            DependencyProperty.Register("CompleteDescriptionFormat", typeof(string), typeof(GraphSlider),
                 new PropertyMetadata((obj, args) =>
                 {
                     var slider = (GraphSlider)obj;
                     slider.SetCompleteDescriptionText(slider.CompleteDescriptionFormat);
                 }));


        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(GraphSlider),
                 new PropertyMetadata((obj, args) =>
                 {
                     var slider = (GraphSlider)obj;
                     if (slider.TitleLabel != null)
                         slider.TitleLabel.Text = (string)args.NewValue;
                 }));



        public string BalanceLegend
        {
            get { return (string)GetValue(BalanceLegendProperty); }
            set { SetValue(BalanceLegendProperty, value); }
        }

        public static readonly DependencyProperty BalanceLegendProperty =
            DependencyProperty.Register("BalanceLegend", typeof(string), typeof(GraphSlider),
                new PropertyMetadata((obj, args) =>
                {
                    var slider = (GraphSlider)obj;
                    slider.SetBalanceLegendText((string)args.NewValue);
                }));

        public DateTime CompleteDate
        {
            get { return (DateTime)GetValue(CompleteDateProperty); }
            set { SetValue(CompleteDateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CompleteDate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CompleteDateProperty =
            DependencyProperty.Register("CompleteDate", typeof(DateTime), typeof(GraphSlider),
                 new PropertyMetadata((obj, args) =>
                 {
                     var slider = (GraphSlider)obj;
                     slider.SetCompleteDescriptionText(slider.CompleteDescriptionFormat);
                     slider.UpdateSlider();
                 }));



        public DateTime StartDate
        {
            get { return (DateTime)GetValue(StartDateProperty); }
            set { SetValue(StartDateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartDate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartDateProperty =
            DependencyProperty.Register("StartDate", typeof(DateTime), typeof(GraphSlider),
                 new PropertyMetadata((obj, args) =>
                 {
                     var slider = (GraphSlider)obj;
                     slider.UpdateSlider();
                 }));



        public int TotalPayments
        {
            get { return (int)GetValue(TotalPaymentsProperty); }
            set { SetValue(TotalPaymentsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TotalPayments.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TotalPaymentsProperty =
            DependencyProperty.Register("TotalPayments", typeof(int), typeof(GraphSlider),
                 new PropertyMetadata((obj, args) =>
                 {
                     var slider = (GraphSlider)obj;
                     slider.UpdateSlider();
                 }));



        public double NextPaymentInterest
        {
            get { return (double)GetValue(NextPaymentInterestProperty); }
            set { SetValue(NextPaymentInterestProperty, value); }
        }

        public static readonly DependencyProperty NextPaymentInterestProperty =
            DependencyProperty.Register("NextPaymentInterest", typeof(double), typeof(GraphSlider),
                new PropertyMetadata((obj, args) =>
                {
                    var slider = (GraphSlider)obj;
                    slider.UpdateSlider();
                }));




        public string XAxisDateFormat
        {
            get { return (string)GetValue(XAxisDateFormatProperty); }
            set { SetValue(XAxisDateFormatProperty, value); }
        }

        // Using a DependencyProperty as the backing store for XAxisDateFormat.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty XAxisDateFormatProperty =
            DependencyProperty.Register("XAxisDateFormat", typeof(string), typeof(GraphSlider),
                new PropertyMetadata((obj, args) =>
                {
                    var slider = (GraphSlider)obj;
                    slider.SetAnumMarks();
                }));




        public DateTime EndDate
        {
            get
            {
                object obj = GetValue(EndDateProperty);
                if (obj == null) return StartDate.AddYears(30); 
                return (DateTime)obj;

            }
            set { SetValue(EndDateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EndDate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EndDateProperty =
            DependencyProperty.Register("EndDate", typeof(DateTime), typeof(GraphSlider),
                new PropertyMetadata((obj, args) =>
                {
                    var slider = (GraphSlider)obj;
                    slider.SetAnumMarks();
                }));



        #endregion



        public override void OnApplyTemplate()
        {
            LineGraphCanvas = (Canvas)GetTemplateChild("LineGraphCanvas");
            CompletedLabel = (TextBlock)GetTemplateChild("Complete");
            TitleLabel = (TextBlock)GetTemplateChild("Title");
            BalanceLegendLabel = (TextBlock)GetTemplateChild("BalanceLegend");
            AxisPath = (Path)GetTemplateChild("Axis");
            SliderCanvas = (Canvas)GetTemplateChild("SliderCanvas");
            GraphLineCanvas = (Canvas)GetTemplateChild("GraphLineCanvas");
            PaymentsPath = (Path)GetTemplateChild("Payments");
            EstimatedTrack = (Path)GetTemplateChild("EstimatedTrack");
            SliderThumb = (Ellipse)GetTemplateChild("SliderThumb");

            Draw();

        }



        private Canvas _lineGraphCanvas;
        private TextBlock _completeLabel;
        private TextBlock _titleLabel;
        private TextBlock _balanceLegendLabel;
        private Path _axisPath;
        private Canvas _sliderCanvas;
        private Canvas _graphLineCanvas;
        private Path _paymentsPath;
        private Path _estimatedTrack;
        private Ellipse _sliderThumb;

        private Ellipse SliderThumb
        {
            get { return _sliderThumb; }
            set { _sliderThumb = value; }
        }

        public Path PaymentsPath
        {
            get { return _paymentsPath; }
            set { _paymentsPath = value; }
        }

        public Path EstimatedTrack
        {
            get { return _estimatedTrack; }
            set { _estimatedTrack = value; }
        }

        public Canvas GraphLineCanvas
        {
            get { return _graphLineCanvas; }
            set { _graphLineCanvas = value; }
        }

        public Canvas LineGraphCanvas
        {
            get { return _lineGraphCanvas; }
            set { _lineGraphCanvas = value; }
        }

        public Path AxisPath
        {
            get { return _axisPath; }
            set { _axisPath = value; }
        }

        public TextBlock CompletedLabel
        {
            get { return _completeLabel; }
            set
            {
                _completeLabel = value;
                SetCompleteDescriptionText(CompleteDescriptionFormat);
            }
        }

        public Canvas SliderCanvas
        {
            get { return _sliderCanvas; }
            set
            {
                if (_sliderCanvas != null)
                {
                    _sliderCanvas.MouseLeftButtonDown -= new MouseButtonEventHandler(_sliderCanvas_MouseLeftButtonDown);
                    _sliderCanvas.MouseLeftButtonUp -= new MouseButtonEventHandler(_sliderCanvas_MouseLeftButtonUp);
                    _sliderCanvas.MouseMove -= new MouseEventHandler(_sliderCanvas_MouseMove);
                }
                _sliderCanvas = value;

                if (_sliderCanvas != null)
                {
                    _sliderCanvas.MouseLeftButtonDown += new MouseButtonEventHandler(_sliderCanvas_MouseLeftButtonDown);
                    _sliderCanvas.MouseLeftButtonUp += new MouseButtonEventHandler(_sliderCanvas_MouseLeftButtonUp);
                    _sliderCanvas.MouseMove += new MouseEventHandler(_sliderCanvas_MouseMove);
                }
            }

        }



        public TextBlock TitleLabel
        {
            get { return _titleLabel; }
            set
            {
                _titleLabel = value;
                if (value != null)
                {
                    _titleLabel.Text = Title;
                }
            }
        }

        public TextBlock BalanceLegendLabel
        {
            get { return _balanceLegendLabel; }
            set
            {
                _balanceLegendLabel = value;
                if (value != null)
                {
                    SetBalanceLegendText(BalanceLegend);
                }
            }
        }

        #region Slider Event Handlers



        void _sliderCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs args)
        {
            isMouseDown = true;

            Canvas can = (Canvas)sender;

            originMouseX = ((MouseEventArgs)args).GetPosition(null).X;

            originMouseY = ((MouseEventArgs)args).GetPosition(null).Y;

            can.CaptureMouse();
        }

        void _sliderCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Canvas can = (Canvas)sender;
            can.ReleaseMouseCapture();
            isMouseDown = false;
        }

        void _sliderCanvas_MouseMove(object sender, MouseEventArgs args)
        {
            // If mouse button is still in down position
            if (isMouseDown)
            {
                // Retrieve current mouse position
                currentMouseX = args.GetPosition(null).X;
                currentMouseY = args.GetPosition(null).Y;

                double deltaX = (currentMouseX - originMouseX);

                // Set slider thumb position
                SetSlider(deltaX);

                // Raise event
                //OnSliderChanged(new EventArgs());
            }
        }

        #endregion

        #region UpdateUI

        // Variables for Mouse Events
      
        bool isMouseDown = false;
        double originMouseX;
        double originMouseY;
        double currentMouseX;
        double currentMouseY;
        // Variable for savings label
        DateTime initialCompletionDate = DateTime.Today;
        double delta;

        double initialOffset = 34.344;


        private void Draw()
        {
            SetAnumMarks();
            UpdateSlider();
        }

        private void SetCompleteDescriptionText(string text)
        {
            if (CompletedLabel != null)
            {
                CompletedLabel.Text = string.Format(text.Replace(@"\n", Environment.NewLine), CompleteDate);
            }
        }

        private void SetBalanceLegendText(string text)
        {
            if (BalanceLegendLabel != null)
            {
                BalanceLegendLabel.Text = text;
            }
        }

        private void UpdateSlider()
        {
            if (AxisPath == null) return;

            double sliderLeft = (double)SliderCanvas.GetValue(Canvas.LeftProperty);
            DateTime estimatedCompletionDate = CompleteDate; // ((MortgageAccount)model).ProjectedPaymentCompleteDate;
            double newPos = DateToPosition(estimatedCompletionDate, AxisPath.ActualWidth, TotalPayments, StartDate);
            SetSliderAbsolute(newPos + initialOffset);
        }

        private DateTime PositionToDate(double pos, double width, int numberOfPayments, DateTime startDate)
        {
            double offset = (numberOfPayments * (pos - initialOffset)) / width;
            int increment = Convert.ToInt32(offset);
            return startDate.AddMonths(increment);
        }

        private double DateToPosition(DateTime date, double width, int numberOfPayments, DateTime startDate)
        {
            System.TimeSpan termPassed = date - startDate;
            System.TimeSpan term = startDate.AddMonths(numberOfPayments) - startDate;
            double percentOfTerm = ((double)termPassed.Days) / ((double)term.Days);
            return width * percentOfTerm;
        }

        public void SetSlider(double deltaX)
        {
            // Find slider thumb and its dimensions

            double sliderThumbWidth = (double)SliderThumb.GetValue(Ellipse.WidthProperty);

            // Find slider bar and its dimensions            
            double sliderBarMin = (double)AxisPath.GetValue(Canvas.LeftProperty)
                                - sliderThumbWidth / 2;
            double sliderBarMax = (double)AxisPath.GetValue(Canvas.LeftProperty)
                                + AxisPath.Width
                                - sliderThumbWidth / 2;

            double sliderMinAllowable = sliderBarMin + 143;

            double newValue = (double)SliderCanvas.GetValue(Canvas.LeftProperty)
                               + deltaX;

            // Ensure that slider thumb doesn`t go beyond min or max
            if (newValue > sliderBarMax)
            {
                newValue = sliderBarMax;
                deltaX = sliderBarMax
                        - (double)SliderCanvas.GetValue(Canvas.LeftProperty);
            }

            if (newValue < sliderMinAllowable)
            {
                newValue = sliderMinAllowable;
                deltaX = sliderMinAllowable
                        - (double)SliderCanvas.GetValue(Canvas.LeftProperty);
            }

            this.delta = deltaX;

            double sliderLeft = (double)SliderCanvas.GetValue(Canvas.LeftProperty);

            bool trackValid = AlterTrack(delta);

            // Reset the location of the object
            if (trackValid)
            {
                // Set Value
                SliderCanvas.SetValue(Canvas.LeftProperty, newValue);

                // Update the beginning position of the mouse
                originMouseX = currentMouseX;
                originMouseY = currentMouseY;

                DateTime completedDate = PositionToDate(sliderLeft + delta, AxisPath.Width, TotalPayments, StartDate);

                CompleteDate = completedDate;
            }
        }

        public void SetSliderAbsolute(double pos)
        {

            DateTime completedDate = PositionToDate(pos, AxisPath.ActualWidth, TotalPayments, StartDate);
            if (completedDate > EndDate) return;

            double sliderLeft = (double)SliderCanvas.GetValue(Canvas.LeftProperty);

            bool trackValid = SetTrack(pos - initialOffset - (SliderThumb.Height / 2), true);

            // Reset the location of the object
            if (trackValid)
            {

                SliderCanvas.SetValue(
                    Canvas.LeftProperty,
                    pos
                    );

                // Update the beginning position of the mouse
                originMouseX = currentMouseX;
                originMouseY = currentMouseY;


                CompleteDate = completedDate;

                // Update initial completion date to the current
                // date at slider
                initialCompletionDate = completedDate;

            }
        }

        public bool AlterTrack(double deltaX)
        {
            bool isValid = false;
            double newTrackRight = 0;

            Path track = EstimatedTrack;
            newTrackRight = (double)track.GetValue(Canvas.WidthProperty);
            newTrackRight += deltaX;

            if (newTrackRight >= 0)
            {
                track.SetValue(Canvas.WidthProperty, newTrackRight);
                isValid = true;
            }

            return isValid;
        }

        public bool SetTrack(double endPos)
        {
            return SetTrack(endPos, false);
        }

        public bool SetTrack(double endPos, bool force)
        {
            bool isValid = false;
            double newTrackRight = 0;
            double minimumPaymentLevel = 0;
            Path track = EstimatedTrack;

            newTrackRight = endPos - (double)PaymentsPath.GetValue(Canvas.WidthProperty);
            if (newTrackRight >= minimumPaymentLevel || force )
            {
                if (newTrackRight > 0)
                {
                    track.SetValue(Canvas.WidthProperty, newTrackRight);
                    isValid = true;
                }
            }
            return isValid;
        }


        private List<TextBlock> _anumDateMarks = new List<TextBlock>();

        private void SetAnumMarks()
        {
            if (LineGraphCanvas == null || AxisPath == null) return;

            double initialOffset = 0;

            int periods = TotalPayments;
            DateTime startDate = StartDate;
            string formatString = XAxisDateFormat;

            ClearExistingAnumBoxes();

            double leftEdge = (double)AxisPath.GetValue(Canvas.LeftProperty);
            double width = AxisPath.ActualWidth;
            double bottom = (double)AxisPath.GetValue(Canvas.TopProperty) + AxisPath.Height;

            int yearSpacing = 5;
            double ticks = periods / 12.0 / Convert.ToDouble(yearSpacing);
            double increment = width / (double)ticks;

            for (int i = 0; i <= ticks; i++)
            {
                double tickCanvasLeft = initialOffset + i * increment;
                DateTime newDate = startDate.AddYears(i * yearSpacing);

                if (newDate > EndDate) break;

                TextBlock newTextBlock = new TextBlock();

                newTextBlock.Height = 23.873;
                newTextBlock.FontSize = 8;
                newTextBlock.TextWrapping = TextWrapping.Wrap;
                newTextBlock.Width = 23.322;

                newTextBlock.SetValue(Canvas.TopProperty, bottom);
                newTextBlock.SetValue(Canvas.LeftProperty, tickCanvasLeft + (leftEdge - newTextBlock.Width / 2));
                newTextBlock.Text = String.Format(formatString, newDate);

                _anumDateMarks.Add(newTextBlock);
                LineGraphCanvas.Children.Add(newTextBlock);
            }
        }

        private void ClearExistingAnumBoxes()
        {
            _anumDateMarks.ForEach(mark => LineGraphCanvas.Children.Remove(mark));
            _anumDateMarks.Clear();
        }


        #endregion
    }

}
